package com.haiyou.sdk.rank;

import java.io.Serializable;
import java.util.Collection;
import java.util.Map;
import java.util.Set;

import org.redisson.api.RScoredSortedSet;
import org.redisson.api.RedissonClient;
import org.redisson.client.protocol.ScoredEntry;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

import com.google.common.collect.Maps;
import com.haiyou.common.util.object.ClassUtils;
import com.haiyou.common.util.time.ServerTime;
import com.haiyou.data.common.cache.redis.RKeys;
import com.haiyou.data.common.cache.redis.key.RedisPrefix;
import com.haiyou.data.common.cache.redis.redisson.RedisRedisson;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;


/**
 * 
 * @Description: 排行榜抽象对象,排行榜的值只存key
 * @author xingyuan
 * @date 2022年3月30日 下午6:48:21
 */
@Slf4j
public abstract class AbstractRank<K extends Serializable & Comparable<K>,V> implements Rank<K, V>{

	/**
	 * 后缀
	 */
	private static final String SCOREDSORTED_SUFFIX_RANK = RedisPrefix.SCOREDSORTED_SUFFIX_RANK;

	public static final long max = 5000000000000l;
	public static final long f =   10000000000000l;
	
	/**
	 * 前缀
	 */
	@Getter
	public String keyPrefix = "";
	
	/**
	 * 排行榜 redisson 对象<br>
	 * 注意:排行榜的值只存key
	 */
	@Getter
	protected RScoredSortedSet<K> scoredSortedSet;

	/**
	 * 排行榜 redis key
	 */
	@Getter
	protected String redisKey;
	
	/**
	 * redisson 客户端
	 */
	@Getter
	protected RedissonClient redissonClient;
	
	/**
	 * 
	 * @param dbType redis 数据源
	 * @param keyPrefix 排行榜唯一标记
	 * @param config 
	 */
	public AbstractRank(String dbType,String keyPrefix) {
		init(dbType, keyPrefix);
	}

	protected void init(String dbType,String keyPrefix) {
		this.keyPrefix = keyPrefix;
		this.redissonClient = RedisRedisson.getRedissonClient(dbType);

		Assert.notNull(redissonClient,"找不到数据源为 dbType:"+dbType+",redisson客户端");

		//组装 redis key
		this.redisKey = RKeys.createRKey(RScoredSortedSet.class, keyPrefix, SCOREDSORTED_SUFFIX_RANK);

		//初始化 redisson 对象
		scoredSortedSet = redissonClient.getScoredSortedSet(this.redisKey);

	}



	
	

	/**
	 * 获取 key 类型
	 * @param clazz
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private Class<K> getKeyClass(Class<?> clazz) {
		Class<?>[] cls = ClassUtils.getTypeArguments(clazz);
		Class<K> clz = (Class<K>) (cls.length == 2 ? cls[0] : null);
		if (clz != null)
			return clz;
		return getKeyClass(clazz.getSuperclass());
	}

	@Override
	public int limit() {
		return 10000;
	}



	@Override
	public double minScored() {
		return 0;
	}


	@Override
	public K lastKey() {
		
		Collection<ScoredEntry<K>> collection = scoredSortedSet.entryRange(0, 0);
		
		if(ObjectUtils.isEmpty(collection)) {
			return null;
		}
		
		return collection.iterator().next().getValue();
	}

	
	/**
	 * 默认返回 -1,列表为null
	 */
	@Override
	public double lastScored() {
		K k = lastKey();
		
		if(ObjectUtils.isEmpty(k)) {
			return -1;
		}
		Double scored = scoredSortedSet.getScore(k);
		
		if(ObjectUtils.isEmpty(scored)) {
			return -1d;
		}
		return scored;
	}


	@Override
	public boolean compare(V v1, V v2) {
		
		double scored1 = calScored(v1);
		double scored2 = calScored(v2);
		
		if(scored1 > scored2) {
			return true;
		}
		
		return false;
	}


	/**
	 * 计算得分<br>
	 * 添加了时间参数,计算时基于时间戳.<br>
	 * 小数点后时间戳<br>
	 * 计算仅保留整数部分
	 * @param v
	 * @return
	 */
	@Override
	public double calScored(V v) {
		
		double score = calScoredByV(v);
		
		if(score == 0d) {
			return 0d;
		}
		
		return score;
		
// 添加时间参数
//		String scoreStr = (long)score +"."+ dValue();
//		
//		return Double.parseDouble(scoreStr);
	}
	
	/**
	 * 基于时间获取小数点后值
	 * @return
	 */
	public long dValue() {
		return  max - ServerTime.timeMillis();
	}

	public void remove(K k) {
		scoredSortedSet.removeAsync(k);
	}
	
	public void remove(V v) {
		scoredSortedSet.removeAsync(getId(v));
	}
	
	public void remove(Set<K> ks) {
		scoredSortedSet.removeAllAsync(ks);
	}
	
	/**
	 * 清空榜单
	 */
	public void clear() {
		scoredSortedSet.clear();
	}
	
	
	/**
	 * 同步操作
	 * @param v
	 * @return
	 */
	public boolean updateRankSync(V v) {
		
		if(ObjectUtils.isEmpty(v)) {
			return false;
		}
		
		double scored = calScored(v);
		
//		if(this.rankConfig.getMinScored() > scored) {
//			//小于最低分限制
//			//移除
//			RankEntity<K> oldRank = rankK(getId(v));
//			
//			if(oldRank != null) {
//				scoredSortedSet.removeAsync(getId(v));
//			}
//			
//			return false;
//		}
		
		//排行榜还不够 直接更新
//		if(scoredSortedSet.size() < this.rankConfig.getLimit() || this.rankConfig.getLimit() == -1) {
//			addRankSync(scored, v);
//			return true;
//		}
//		
//		//最后一名的分值
//		double lastScored = lastScored();
//		
//		if(lastScored > scored) {
//			return false;
//		}
		
		addRankSync(scored, v);
		
		return true;
	}
	
	@Override
	public boolean updateRank(V v) {
		
		if(ObjectUtils.isEmpty(v)) {
			return false;
		}
		
		double scored = calScored(v);
		
//		if(this.rankConfig.getMinScored() > scored) {
//			//小于最低分限制
//			//移除
//			RankEntity<K> oldRank = rankK(getId(v));
//			
//			if(oldRank != null) {
//				scoredSortedSet.removeAsync(getId(v));
//			}
//			
//			return false;
//		}
		
		//排行榜还不够 直接更新
//		if(scoredSortedSet.size() < this.rankConfig.getLimit() || this.rankConfig.getLimit() == -1) {
//			addRank(scored, v);
//			return true;
//		}
//		
//		//最后一名的分值
//		double lastScored = lastScored();
//		
//		if(lastScored > scored) {
//			return false;
//		}
		
		addRank(scored, v);
		
		return true;
	}
	
	/**
	 * 添加到排行榜
	 * @param scored
	 * @param k
	 */
	public void addRank(double scored,V v) {
		//进行排行榜更新
		scoredSortedSet.addScoreAsync(getId(v), scored);
		
		reSize();
	}
	
	
	/**
	 * 添加道排行榜同步模式
	 * @param scored
	 * @param v
	 */
	public void addRankSync(double scored,V v) {
		scoredSortedSet.addScore(getId(v), scored);
		reSize();
	}
	
	/**
	 * 批量添加
	 * @param entitys
	 */
	public void addAllRank(Map<K, Double> entitys) {
		scoredSortedSet.addAllAsync(entitys);
		reSize();
	}
	
	/**
	 * 保证列队大小
	 */
	protected void reSize() {
		
		if(this.limit() <= 0) {
			return ;
		}
		
		while (scoredSortedSet.size() > this.limit()) {
			scoredSortedSet.pollFirstAsync();
		}
	}
	
	@Override
	public int size() {
		return scoredSortedSet.size();
	}
	
	
	/**
	 * 根据步长计算最大page
	 * @param step
	 * @return
	 */
	public int maxPage(int step) {
		
		int size = size();
		
		if(size == 0) {
			return 1;
		}
		
		int n = size % step;
		
		if(n == 0) {
			return (size / step);
		}else{
			return (size / step) + 1;
		}
	}
	

	@Override
	public Map<K, RankEntity<K>> rangeUp(int page, int step) {
		
		if(page < 0) {
			page = 0;
		}
		
		if(step < 0) {
			step = 1;
		}
		
		int size = size();
	
		if(size == 0) {
			return Maps.newHashMap();
		}
		
		//起始点
		int index1 = page * step;
		
		//结束点
		int index2 = index1 + step - 1;
		
		index2 = Math.min(index2, size);
		
		Collection<ScoredEntry<K>> collection = scoredSortedSet.entryRange(index1, index2);

		Map<K, RankEntity<K>> maps = Maps.newHashMap();
		
		for (ScoredEntry<K> scoredEntry : collection) {
			RankEntity<K> entity = newRankEntity(index1 + 1, scoredEntry.getValue());
			entity.setScore(scoredEntry.getScore());
			maps.put(scoredEntry.getValue(), entity);
			index1++;
		}
		
		return maps;
	}
	

	@Override
	public Map<K, RankEntity<K>> range(int page, int step) {
		
		if(page < 0) {
			page = 0;
		}
		
		if(step < 0) {
			step = 1;
		}
		
		int size = size();
	
		if(size == 0) {
			return Maps.newHashMap();
		}
		
		//起始点
		int index1 = page * step;
		
		//结束点
		int index2 = index1 + step - 1;
		
		index2 = Math.min(index2, size);
		
		Collection<ScoredEntry<K>> collection = scoredSortedSet.entryRangeReversed(index1, index2);

		Map<K, RankEntity<K>> maps = Maps.newHashMap();
		
		for (ScoredEntry<K> scoredEntry : collection) {
			RankEntity<K> entity = newRankEntity(index1 + 1, scoredEntry.getValue());
			entity.setScore(scoredEntry.getScore());
			maps.put(scoredEntry.getValue(), entity);
			index1++;
		}
		
		return maps;
	}
	
	
	/**
	 * 获得对应数量的排行榜数据
	 * 
	 * @param size
	 * @return
	 */
	public Collection<ScoredEntry<K>> getRankAllData(int size) {
		return scoredSortedSet.entryRangeReversed(0, size);
	}
	
	/**
	 * 创建排行榜对象
	 * @param <E>
	 * @param ranking
	 * @param e
	 * @return
	 */
	public <E> RankEntity<E> newRankEntity(int ranking,E e){
		RankEntity<E> rankEntity = new RankEntity<E>();
		rankEntity.setRanking(ranking);
		rankEntity.setV(e);
		return rankEntity;
	}
	
	@Override
	public RankEntity<K> rankK(K k) {
	
		Integer rank = scoredSortedSet.revRank(k);
		Double value = scoredSortedSet.getScore(k);
		if(ObjectUtils.isEmpty(rank)) {
			return null;
		}else {
			RankEntity<K> enitiy = newRankEntity(rank + 1, k);
			enitiy.setScore(value);
			return enitiy;
		}
	}
	
	
	/**
	 * 计算 V 的得分
	 * @param v
	 * @return
	 */
	public abstract double calScoredByV(V v);

	
	/**
	 * 排行榜id 区分是什么排行榜
	 * @return
	 */
	public abstract int getRankType();
	

}
